<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Article</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
<link rel="stylesheet" href= "../assets/markdownLayout/git_md.css">
<link rel="stylesheet" href= "../assets/theme/theme_fresh.css">
<link rel="stylesheet" href= "../assets/hightlightStyle/atom-one-light.css">

<style>
    .markdown-body{
        box-sizing: border-box;
        min-width: 200px;
        max-width: 980px;
        margin: 0 auto;
        padding: 45px;
    }

    @media (max-width: 767px) {
        .markdown-body {
            padding: 15px;
        }
    }
</style>


</head>
<body>
    
    <!--header-->
    <div class="header">
    <header>
        <div class="logo"><a href="../index.html"><span>hh2o4</span></a></div>
        <nav>
            
                <li> <a href="#">articles</a> </li>
                <li> <a href="../contact.html">contact</a> </li>
                <li> <a href="#">pictures</a> </li>
            
        </nav>
    </header>
</div>

    <!--content-->
    <div class="content">
        <h1>对象的创建以及继承</h1>
        <div class="info"><span class="date">发表时间: 2017-08-29</span></div>
        <article class="markdown-body">
            <p>在第一次学习这部分内容的时候，有好多地方有疑问，在第二次学习这部分内容的时候，认识更全面和深刻了一些，特别总结了一下对象创建和继承的方法，并附上了每种方法自己的实现。</p>
<h2 id="-">一. 对象的创建</h2>
<h3 id="1-">1. 对象字面量</h3>
<p>通过对象字面量的语法定义</p>
<pre><code class="lang-js"><span class="hljs-attribute">var o</span> = {
  name : <span class="hljs-string">"person"</span>,
  age : <span class="hljs-string">"29"</span>
};
</code></pre>
<p>缺点很明显，代码重用效果差。</p>
<h3 id="2-">2. 工厂模式</h3>
<p>通过一个工厂函数，生成一个对象并返回。该对象的相关属性，由工厂函数传进来的参数进行初始化。</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createPerson</span>(<span class="hljs-params">name, age</span>)</span>{
    <span class="hljs-keyword">var</span> o = {};
    o.name = name;
    o.age = age;

    o.sayName = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"hello "</span> + <span class="hljs-keyword">this</span>.name);
    }
    <span class="hljs-keyword">return</span> o;
}

<span class="hljs-keyword">var</span> person1 = createPerson(<span class="hljs-string">"person1"</span>, <span class="hljs-string">"29"</span>);
<span class="hljs-keyword">var</span> person2 = createPerson(<span class="hljs-string">"person2"</span>, <span class="hljs-string">"30"</span>);
</code></pre>
<p>工厂模式跟之后提到的构造函数模式原理相似，区别是工厂模式下，不能方便地识别实例对象的类型（即不能通过instanceof操作符方便地知道某个实例对象是不是属于某个类别）。</p>
<h3 id="3-">3. 构造函数模式</h3>
<p>构造函数模式的特点是，需要用new操作符调用构造函数来生成实例对象。在new操作符的作用下，实例对象会有一个内部指针（[[prototype]]或者<strong>proto</strong>）指向构造函数的prototype，instanceof操作符会根据判断构造函数的prototype在不在实例对象的原型链上，判断实例对象属不属于构造函数标识的类型。而实例对象的原型链，是在new的时候由[[prototype]]内部指针串起来的。</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Person</span>(<span class="hljs-params">name, age</span>)</span>{
    <span class="hljs-keyword">this</span>.name = name;
    <span class="hljs-keyword">this</span>.age = age;

    <span class="hljs-keyword">this</span>.sayName = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">this</span>.name);
    }
}

<span class="hljs-keyword">var</span> person1 = <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"person1"</span>, <span class="hljs-string">"29"</span>);
<span class="hljs-keyword">var</span> person2 = <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"person2"</span>, <span class="hljs-string">"30"</span>);

person1 <span class="hljs-keyword">instanceof</span> Person; <span class="hljs-comment">//true</span>
</code></pre>
<p>由于new操作符会把this指向新创建的实例对象，所以用new操作符调用构造函数时，函数里的赋值就相当于对实例对象属性的赋值。</p>
<p>往往构造函数里没有return语句，因为构造函数通常以new操作符调用，而当以new操作符调用时，若构造函数返回原始值，则原始值被忽略，会返回一个由构造函数生成的实例（跟没有返回值的情况一样），可以具有构造函数原型的方法和属性；</p>
<p>而当构造函数返回引用类型时（Array、String、Function等），会返回这个任意的引用类型值，而这个值无法获得构造函数原型的方法或属性，即该对象不在和构造函数相关的原型链上。</p>
<p>构造函数模式有以下几个问题：</p>
<ol>
<li>实例间一般不共享属性，但是会共享方法，每次用new调用构造函数都会生成一个新的但功能完全相同的函数对象（方法），会造成内存的浪费，也没有真正体现共享的好处。</li>
<li>构造函数的调用方式不当可能会污染全局空间（非严格模式），或者造成错误（严格模式）。具体来说，当不用new操作符调用构造函数时，非严格模式下，this会指向window、global全局对象；严格模式下会抛出错误，因为严格模式下的全局对象为undefined，不能对undefined设置属性值。解决这个问题，需要创建作用域安全的构造函数：</li>
</ol>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Person</span><span class="hljs-params">(name, age)</span> </span>{
    <span class="hljs-keyword">if</span>(!(<span class="hljs-keyword">this</span> <span class="hljs-keyword">instanceof</span> Person)) {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Person(name, age);
        <span class="hljs-comment">//或者</span>
        <span class="hljs-comment">//return new Person(...arguments);</span>
    }<span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">this</span>.name = name;
        <span class="hljs-keyword">this</span>.age = age;
    }
}
</code></pre>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Person</span>(<span class="hljs-params">name, age</span>) </span>{
    <span class="hljs-keyword">if</span>(!(<span class="hljs-keyword">this</span> <span class="hljs-keyword">instanceof</span> Person)) {
        <span class="hljs-keyword">var</span> o = <span class="hljs-built_in">Object</span>.create(Person.prototype);
        Person.apply(o,[].slice.call(<span class="hljs-built_in">arguments</span>,<span class="hljs-number">0</span>));
        <span class="hljs-keyword">return</span> o;
    }<span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">this</span>.name = name;
        <span class="hljs-keyword">this</span>.age = age;
    }
}
</code></pre>
<ol>
<li>为了解决重复定义对象方法的问题，可能考虑会一次创建多个全局函数，在构造函数中引用这些全局函数，但这样失去了封装性，而且污染全局命名空间。这个问题可以通过原型模式来解决。</li>
</ol>
<h3 id="4-">4. 原型模式</h3>
<p>原型模式，就是定义一个空的构造函数，属性和方法的初始化都通过构造函数的prototype来设置。各实例通过原型共享属性和方法，并且这些共享的值都存在于构造函数的prototype下，不污染全局空间。</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Person</span>(<span class="hljs-params"></span>) </span>{
}

Person.prototype.name = <span class="hljs-string">"person"</span>;
Person.prototype.age = <span class="hljs-number">29</span>;

Person.sayName = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">this</span>.name);
}

<span class="hljs-keyword">var</span> person1 = <span class="hljs-keyword">new</span> Person();
person1.sayName();
</code></pre>
<p>这种模式的缺点就是有属性是每个实例特有的，不需要共享，最好能在构造函数创建对象的时候初始化这些不共享的属性，否则创建对象后，还要显式地配置对象的属性，比较麻烦。</p>
<p>这里需要注意，原型链和只属于实例中的<strong>可枚举</strong>属性可以通过for/in列出，或者通过in操作符的测试。而object.keys()可以列出<strong>只属于</strong>实例的<strong>可枚举</strong>属性，该方法返回一个字符串数组。</p>
<h3 id="5-">5. 组合构造函数和原型模型</h3>
<p>在原型模式的基础上，把不共享的属性的初始化放到了构造函数中，作为实例自己的属性，而共享方法的定义放到了构造函数的原型中，使得实例可以共享方法。这是目前用得最广泛的自定义类型方法。</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Person</span>(<span class="hljs-params">name, age</span>) </span>{
    <span class="hljs-keyword">this</span>.name = name;
    <span class="hljs-keyword">this</span>.age = age;
}

Person.prototype.sayName = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">this</span>.name);
}

Person.prototype.kind = <span class="hljs-string">"human"</span>;

<span class="hljs-keyword">var</span> person1 = <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"person1"</span>, <span class="hljs-number">29</span>);
</code></pre>
<p>该方法也有缺点，就是要在构造函数外面定义Person.prototype的各属性，显得封装性不是很好。动态原型模式用以解决这个问题。</p>
<h3 id="6-">6. 动态原型模式</h3>
<p>将在构造函数prototype上的操作放到构造函数内部,在第一次调用构造函数时，就能生成原型方法。</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Person</span>(<span class="hljs-params">name, age</span>) </span>{
    <span class="hljs-keyword">this</span>.name = name;
    <span class="hljs-keyword">this</span>.age = age;

    <span class="hljs-comment">//让方法不要重新定义，以免每次调用Person时生成多余的对象。</span>
    <span class="hljs-keyword">if</span> (!Person.prototype.sayName) {
        Person.prototype.sayName = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
            <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">this</span>.name);
        };
    }

    <span class="hljs-keyword">if</span> (!Person.prototype.kind) {
        Person.prototype.kind = <span class="hljs-string">"human"</span>;
    }
}

<span class="hljs-keyword">var</span> person1 = <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"person1"</span>, <span class="hljs-number">20</span>);

<span class="hljs-built_in">console</span>.log(person1);  <span class="hljs-comment">//Person { name: 'person1', age: 20 }</span>

<span class="hljs-built_in">console</span>.log(person1.kind);  <span class="hljs-comment">//human</span>

person1.sayName();  <span class="hljs-comment">//person1</span>
</code></pre>
<p>该方法跟组合构造函数和原型模式相比，就是编程习惯上更类似于传统的OO编程模式。</p>
<h3 id="7-">7. 寄生构造函数模式</h3>
<p>该模式跟工厂模式创建对象的步骤几乎一样，都是在一个函数内创建一个对象，对该对象各属性进行赋值，并返回该对象，只是调用时用new操作符调用构造函数。也存在和工厂模式一样的问题：<strong>实例间不共享方法，造成空间浪费，而且和构造函数的原型之间没有关系，不能通过instanceof操作符判断实例的类型</strong>。</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Person</span>(<span class="hljs-params">name, age</span>) </span>{
    <span class="hljs-keyword">var</span> o = {};
    o.name = name;
    o.age = age;

    o.sayName = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
        <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">this</span>.name);
    };

    <span class="hljs-keyword">return</span> o;
}

<span class="hljs-keyword">var</span> person1 = <span class="hljs-keyword">new</span> Person();
</code></pre>
<h3 id="8-">8. 稳妥构造函数模式</h3>
<p>这种模式用于产生一个稳妥对象，该对象的属性不共享，只能通过调用方法进行访问。稳妥构造函数的写法与工厂模式类似，区别是不用this访问属性。<strong>注：不用new操作符调用构造函数。</strong></p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Person</span>(<span class="hljs-params">name, age</span>) </span>{
    <span class="hljs-keyword">var</span> o = {};
    <span class="hljs-keyword">var</span> job = <span class="hljs-string">"js engineer"</span>;

    o.sayName = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
        <span class="hljs-built_in">console</span>.log(name);
    };

    o.sayAge = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-built_in">console</span>.log(age);
    };

    o.sayJob = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-built_in">console</span>.log(job);
    };

    <span class="hljs-keyword">return</span> o;
}

<span class="hljs-keyword">var</span> person1 = Person(<span class="hljs-string">"person1"</span>, <span class="hljs-number">20</span>);

<span class="hljs-built_in">console</span>.log(person1.name);  <span class="hljs-comment">//undefined</span>
person1.sayName();  <span class="hljs-comment">//person1</span>
</code></pre>
<p>该方法生成的对象跟构造函数的prototype之间没有关系，无法用instanceof操作符判断对象类型。</p>
<h2 id="-">二. 继承</h2>
<blockquote>
<p>传统OO语言都支持两种继承方式：接口继承和实现继承。接口继承只继承方法签名，而实现继承则继承实际的方法。由于js中函数没有签名，所以无法实现接口继承，只支持实现继承，而且其实现继承主要是依靠原型链来实现。——摘自《JavaScript高级程序设计》</p>
</blockquote>
<h3 id="1-">1. 原型链继承</h3>
<p>该方法的思想可以概括为：写一个子类的构造函数，改变这个子类构造函数的prototype为一个父类的实例，这样通过原型链，子类的实例可以通过自己的[[prototype]]指针，找到父类实例，拥有父类实例的全部属性和方法。</p>
<pre><code class="lang-js"><span class="hljs-comment">//定义父类</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Human</span>(<span class="hljs-params">age</span>) </span>{
    <span class="hljs-keyword">this</span>.age = age;
}

Human.prototype.sayAge = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">this</span>.age);
};

<span class="hljs-comment">//定义子类</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Girl</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">this</span>.gender = <span class="hljs-string">"girl"</span>;
}

<span class="hljs-comment">//让子类拥有父类实例的方法和属性。</span>
Girl.prototype = <span class="hljs-keyword">new</span> Human(<span class="hljs-number">20</span>);

<span class="hljs-keyword">var</span> girl1 = <span class="hljs-keyword">new</span> Girl();

<span class="hljs-built_in">console</span>.log(girl1.gender);

girl1.sayAge();
</code></pre>
<p>这种继承方式并不常用，因为通过把子类构造函数的prototype设为父类的实例，当父类实例中有类型为引用值的属性时，某个子类共享并修改了这个属性时，会导致其他的子类实例在同样共享父类实例属性的时候相互影响，比如：</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Super</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">this</span>.colors = [<span class="hljs-string">"red"</span>, <span class="hljs-string">"yellow"</span>];
}

Super.prototype.addColor = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">color</span>) </span>{
    <span class="hljs-keyword">this</span>.colors.push(<span class="hljs-built_in">color</span>);
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Sub</span>(<span class="hljs-params">name</span>) </span>{
    <span class="hljs-keyword">this</span>.name = name;
}

Sub.prototype = <span class="hljs-keyword">new</span> Super();

<span class="hljs-keyword">var</span> sub1 = <span class="hljs-keyword">new</span> Sub(<span class="hljs-string">"sub1"</span>);
<span class="hljs-keyword">var</span> sub2 = <span class="hljs-keyword">new</span> Sub(<span class="hljs-string">"sub2"</span>);

sub1.addColor(<span class="hljs-string">"blue"</span>);
<span class="hljs-built_in">console</span>.log(sub1.colors);  <span class="hljs-comment">//[red, yellow, blue]</span>

sub2.addColor(<span class="hljs-string">"black"</span>);
<span class="hljs-built_in">console</span>.log(sub2.colors);  <span class="hljs-comment">//[red, yellow, blue, black]</span>
</code></pre>
<p>本意是想让两个不同的子类实例对自己的colors属性进行操作，结果其中一个实例对colors的操作影响了另一个实例的结果，另外，原型链继承不能通过子类构造函数向父类构造函数传递参数（不能通过子类构造函数，对从父类继承过来的属性值进行初始化）。应采用一定的方法，使子类构造函数能初始化从父类实例中继承来的属性值，这样每个子类实例之间就有了各自的属性值，不会相互影响了。用于解决上述问题的方法，叫借用构造函数继承方法。</p>
<h3 id="2-">2. 借用构造函数继承</h3>
<p>这种方法的主要思想就是在子类构造函数中调用父类构造函数，对实例属性进行初始化。</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Super</span>(<span class="hljs-params">name, age</span>) </span>{
    <span class="hljs-keyword">this</span>.name = name;
    <span class="hljs-keyword">this</span>.age = age;
    <span class="hljs-keyword">this</span>.colors = [<span class="hljs-string">"red"</span>, <span class="hljs-string">"yellow"</span>];;

    <span class="hljs-keyword">this</span>.sayName = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">this</span>.name);
    };

    <span class="hljs-keyword">this</span>.addColor = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">color</span>)</span>{
        <span class="hljs-keyword">this</span>.colors.push(<span class="hljs-built_in">color</span>);
    };
}

<span class="hljs-comment">//继承Super的属性，但因为是重新调用了Super，所以每个子类实例的colors属性值都是不一样的引用类型值。</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Sub</span>(<span class="hljs-params">name, age</span>)</span>{
    Super.call(<span class="hljs-keyword">this</span>, name, age);
}

<span class="hljs-keyword">var</span> sub1 = <span class="hljs-keyword">new</span> Sub(<span class="hljs-string">"sub1"</span>, <span class="hljs-number">20</span>);
<span class="hljs-keyword">var</span> sub2 = <span class="hljs-keyword">new</span> Sub(<span class="hljs-string">"sub2"</span>, <span class="hljs-number">21</span>);

sub1.addColor(<span class="hljs-string">"blue"</span>);
<span class="hljs-built_in">console</span>.log(sub1.colors);  <span class="hljs-comment">//[red, yellow, blue]</span>

sub2.addColor(<span class="hljs-string">"black"</span>);
<span class="hljs-built_in">console</span>.log(sub2.colors);  <span class="hljs-comment">//[red, yellow, black]</span>

sub1.sayName(); <span class="hljs-comment">//sub1</span>
sub2.sayName(); <span class="hljs-comment">//sub2</span>
</code></pre>
<p>这种方法有个明显的缺点，跟构造函数创建对象一样，需要把方法的定义都写在构造函数中，每次生成实例，都要把同样功能的函数对象也生成一个，做不到函数的复用，因此这个方法也不常用。最常用的方法是下一节要说的组合继承。</p>
<h3 id="3-">3. 组合继承</h3>
<p>该方法结合了原型链继承和借用构造函数继承两种方法的优点，可以类比“组合构造函数模式和原型模式”的创建对象的方法。主要思想是，将属性的定义放到构造函数中，而方法的定义放到构造函数的原型中，通过原型链继承。</p>
<pre><code class="lang-js"><span class="hljs-comment">//定义父类</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Super</span>(<span class="hljs-params">name, age</span>) </span>{
    <span class="hljs-keyword">this</span>.name = name;
    <span class="hljs-keyword">this</span>.age = age;
    <span class="hljs-keyword">this</span>.color = [<span class="hljs-string">"red"</span>, <span class="hljs-string">"yellow"</span>];
}

Super.prototype.sayName = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">this</span>.name);
};

Super.prototype.addColor = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">color</span>) </span>{
    <span class="hljs-keyword">this</span>.colors.push(<span class="hljs-built_in">color</span>);  
};

<span class="hljs-comment">//定义子类</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Sub</span>(<span class="hljs-params">name, age</span>) </span>{
    Super.call(<span class="hljs-keyword">this</span>, name, age);
};

Sub.prototype = <span class="hljs-keyword">new</span> Super();

<span class="hljs-keyword">var</span> sub1 = <span class="hljs-keyword">new</span> Sub(<span class="hljs-string">"sub1"</span>, <span class="hljs-number">20</span>);
<span class="hljs-keyword">var</span> sub2 = <span class="hljs-keyword">new</span> Sub(<span class="hljs-string">"sub2"</span>, <span class="hljs-number">29</span>);

<span class="hljs-comment">//实例的colors属性互不影响</span>
sub1.addColor(<span class="hljs-string">"blue"</span>);
<span class="hljs-built_in">console</span>.log(sub1.colors);  <span class="hljs-comment">//[red, yellow, blue]</span>
<span class="hljs-built_in">console</span>.log(sub2.colors);  <span class="hljs-comment">//[red, yellow]</span>

<span class="hljs-comment">//共享sayName方法</span>
sub1.sayName();  <span class="hljs-comment">//sub1</span>
sub2.sayName();  <span class="hljs-comment">//sub2</span>
</code></pre>
<p>这个方法的缺点是，在将Sub的prototype设为Super实例的时候，还是会对Sub.prototype一系列的属性（比如name, age, color）进行初始化，尽管这些属性会被Sub实例中的同名属性覆盖掉，看起来对实例化子类没有什么影响，但是这种申请了空间存放并不需要的值的做法，是一种浪费。</p>
<h3 id="4-">4. 寄生组合式继承</h3>
<p>实际上，子类原型（如上例中Sub的prototype）需要指向的只是一个包含了父类（如上例中的Super）原型中定义的方法的对象，这个对象可以是父类的实例，也可以是能访问到父类原型方法的空对象。而组合继承中使用了父类的实例，造成了多余的属性定义；寄生组合式继承则通过使用一个能访问父类原型方法的空对象，来避免这个问题。</p>
<pre><code class="lang-js"><span class="hljs-comment">//定义父类</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Super</span>(<span class="hljs-params">name</span>) </span>{
    <span class="hljs-keyword">this</span>.name = name;
    <span class="hljs-keyword">this</span>.colors = [<span class="hljs-string">"red"</span>, <span class="hljs-string">"yellow"</span>];
}

Super.prototype.sayName = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">this</span>.name);
};

Super.prototype.addColor = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">color</span>) </span>{
    <span class="hljs-keyword">this</span>.colors.push(<span class="hljs-built_in">color</span>);  
};

<span class="hljs-comment">//定义子类</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Sub</span>(<span class="hljs-params">name, age</span>) </span>{
    Super.call(<span class="hljs-keyword">this</span>, name);
    <span class="hljs-keyword">this</span>.age = age;
}

<span class="hljs-comment">//定义一个函数，以获得能访问父类原型方法的空对象（[[prototype]]指向父类原型）。</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createObject</span>(<span class="hljs-params">o</span>) </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">F</span>(<span class="hljs-params"></span>) </span>{};
    F.prototype = o;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> F();
}

Sub.prototype = createObject(Super.prototype);

Sub.prototype.sayAge = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">this</span>.age);    
};

<span class="hljs-keyword">var</span> sub1 = <span class="hljs-keyword">new</span> Sub(<span class="hljs-string">"sub1"</span>, <span class="hljs-number">20</span>);
<span class="hljs-keyword">var</span> sub2 = <span class="hljs-keyword">new</span> Sub(<span class="hljs-string">"sub2"</span>, <span class="hljs-number">30</span>);

sub1.addColor(<span class="hljs-string">"color"</span>);
<span class="hljs-built_in">console</span>.log(sub1.colors);  <span class="hljs-comment">//[ 'red', 'yellow', 'black' ]</span>
<span class="hljs-built_in">console</span>.log(sub2.colors);  <span class="hljs-comment">//[ 'red', 'yellow' ]</span>

sub1.sayAge();  <span class="hljs-comment">//20</span>
sub2.sayAge();  <span class="hljs-comment">//30</span>
</code></pre>
<h3 id="5-">5. 原型式继承</h3>
<p>主要思想是写一个函数，传入一个对象，并返回一个新对象，这个新对象可以访问到传入对象上的属性和方法。</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createObject</span><span class="hljs-params">(o)</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">F</span><span class="hljs-params">()</span> </span>{};
    F.prototype = o;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> F();
}
</code></pre>
<p>ES5对这种继承方式进行了规范化，提出了Object.create()方法，跟上述createObject函数做的工作是类似的，不过Object.create方法还可以传入第二个参数，用于对新对象的属性进行赋值。</p>
<h3 id="6-">6. 寄生式继承</h3>
<p>跟工厂模式很类似，通过原型式继承得到一个新对象，再对这个对象做一些增强，并返回这个增强的对象。</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createNewObject</span>(<span class="hljs-params">o</span>) </span>{
    <span class="hljs-keyword">var</span> anotherObj = <span class="hljs-built_in">Object</span>.create(o);
    anotherObj.sayName = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">name</span>) </span>{
        <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">this</span>.name);
    };

    <span class="hljs-keyword">return</span> anotherObj;
}
</code></pre>
<p>同样具有工厂模式的缺点，不再赘述。</p>

        </article>
    </div>

    <!--footer-->
    <footer>
    
    <p>©2017-2018 &nbsp; (o^.^o) &nbsp; hh2o4</p>
    <p>Themed by hh2o4</p>
    
</footer>
    
</body>
</html>